// SPDX-License-Identifier: Mulan PSL v2
/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This software is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use heck::ToShoutySnakeCase;
use proc_macro::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Fields};

pub fn derive_enum_consts_impl(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let enum_name = &input.ident;

    let variants = match input.data {
        Data::Enum(ref data) => &data.variants,
        _ => {
            return syn::Error::new_spanned(input, "EnumConsts can only be used on enums")
                .to_compile_error()
                .into();
        }
    };

    let mut repr_type = None;

    for attr in &input.attrs {
        if attr.path().is_ident("repr") {
            let parse_result = attr.parse_nested_meta(|meta| {
                if let Some(ident) = meta.path.get_ident() {
                    let s = ident.to_string();
                    if matches!(
                        s.as_str(),
                        "u8" | "u16"
                            | "u32"
                            | "u64"
                            | "u128"
                            | "usize"
                            | "i8"
                            | "i16"
                            | "i32"
                            | "i64"
                            | "i128"
                            | "isize"
                    ) {
                        repr_type = Some(ident.clone());
                    }
                }
                Ok(())
            });

            if let Err(e) = parse_result {
                return e.to_compile_error().into();
            }
        }
    }

    let const_type = repr_type.map(|t| quote! { #t }).unwrap_or(quote! { isize });

    let consts = variants.iter().map(|variant| {
        if !matches!(variant.fields, Fields::Unit) {
            return syn::Error::new_spanned(
                variant,
                "EnumConsts only supports unit variants (no fields)",
            )
            .to_compile_error();
        }

        let variant_name = &variant.ident;

        let const_name = variant_name.to_string().to_shouty_snake_case();
        let const_ident = format_ident!("{}", const_name);

        let doc_attrs = variant
            .attrs
            .iter()
            .filter(|attr| attr.path().is_ident("doc"));

        quote_spanned! {variant.span()=>
            #(#doc_attrs)*
            pub const #const_ident: #const_type = Self::#variant_name as #const_type;
        }
    });

    let expanded = quote! {
        impl #enum_name {
            #(#consts)*
        }
    };

    TokenStream::from(expanded)
}
